/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package simulation;

import java.text.MessageFormat;
import logging.LogSetup;
import org.apache.log4j.Logger;

/**
 *
 * @author Danielondon
 */
public class SimpleChargeStrategy implements IChargeStrategy {
    protected Logger logger = LogSetup.getLogger();
    private String strategyName;
    private int slotSize;
    private String carName;
    /**
     * Creates a new Charge Strategy
     * @param strategyName 
     */
    public SimpleChargeStrategy(int slotSize, String carName) {
        this.strategyName = "Simple Strategy"; 
        this.slotSize = slotSize;
        this.carName = carName;
    }
    
    /**
     * Gets the results of the strategy 
     * @param market
     * @param drivingPattern
     * @param initialStateOfCharge
     * @param carSpecs
     * @param totalSlots
     * @return
     * @throws UnfeasibleException 
     */
    @Override
    public SimulationResult getResult( IMarket market, IDrivingPattern drivingPattern, 
                                StateOfCharge initialStateOfCharge, 
                                ICarSpecs carSpecs, int totalSlots) throws UnfeasibleException {
        
        SimulationResult result = new SimulationResult(carSpecs, totalSlots, drivingPattern.getName(), this.getStrategyName());        
        double[] stateOfCharge = new double[totalSlots];
        double[] consumption = new double[totalSlots];
        double[] cost = new double[totalSlots];
        double[] charging = new double[totalSlots];
        
        logger.info("Car Specs " + carSpecs.toString());
        
        // INITIALIZES FIRST STATE. We have to see a work aroung, to avoid these 
        // intitial calls, since the only value avoiding that is the initial state
        // of charge.
        consumption[0] = this.getConsumption(
                                    0,
                                    drivingPattern,
                                    carSpecs,
                                    initialStateOfCharge.getValue()                                    
                );
     
        // only counts when is not connected and there is consumption.
        cost[0] = this.getCost(
                            drivingPattern.getIsConnected(0),
                            consumption[0], 
                            market.getPrice(0)                            
                );
        
        // State of Charge SoC
        stateOfCharge[0] = this.getStateOfCharge(
                                    initialStateOfCharge.getValue(),
                                    consumption[0],
                                    carSpecs.getChargingEfficiency()
                );
        
        charging[0] = Math.max(0, consumption[0]); 
        
        logger.info(MessageFormat.format("0 SoC(s) {0} Consumption(x) {1} Cost (q) {2}", stateOfCharge[0], consumption[0], cost[0]));
        for (int i = 1 ; i < totalSlots; i++){
            // Consumption 
            consumption[i] = this.getConsumption(
                                    i,
                                    drivingPattern, 
                                    carSpecs,
                                    stateOfCharge[i-1]
                );
            // Cost
            cost[i] = this.getCost(
                            drivingPattern.getIsConnected(i),
                            consumption[i], 
                            market.getPrice(i)                            
                );
            
            // SoC
            stateOfCharge[i] = this.getStateOfCharge(
                                    stateOfCharge[i-1],
                                    consumption[i],
                                    carSpecs.getChargingEfficiency()
                );
        
            charging[i] = Math.max(0, consumption[i]); 
            
            logger.info(MessageFormat.format("{0} SoC(s) {1} Consumption(x) {2} Cost (q) {3}", i, stateOfCharge[i], consumption[i], cost[i]));
            
            if (stateOfCharge[i] < carSpecs.getMinimumBatteryCharge()){
                //throw new UnfeasibleException("The Battery charge is less than zero");
                logger.error("The Battery charge is less than zero");
                result.setIsFeasible(false);
            }
        }
        
        
        result.setStateOfEnergy(stateOfCharge);
        result.setConsumption(consumption);
        result.setCost(cost);
        result.setCharging(charging);
        result.setMarket(market.getAllPrices());
        return result;
    }
       
    private double getStateOfCharge(double previousStateOfCharge, 
            double consumption, double eta) {
        logger.info(MessageFormat.format("State Of Charge prevSoC {0} consumption {1}  eta {2}", previousStateOfCharge, consumption, eta));
        double newStateOfCharge = previousStateOfCharge + eta*consumption ;
        return newStateOfCharge;
    }
        
    private double getConsumption(
            int index,
            IDrivingPattern drivingPattern,
            ICarSpecs carSpecs,
            double previousStateOfCharge) {
        int isConnected = drivingPattern.getIsConnected(index);
        double kilometersPerSlot = drivingPattern.getKilometers(index);
        logger.info(MessageFormat.format("Consumption isConnected {0} " + 
                "kilometersPerSlot {1}  consumptionPerKilometer {2} " + ""
                + "maxChargePerSlot {3} previousStateOfCharge {4} batteryCapacity {5}", 
                isConnected, kilometersPerSlot, 
                carSpecs.getPowerConsumptionPerKilometer(),
                carSpecs.getMaxChargingPerSlot(CommonResources.DEFAULT_INTERVAL), 
                previousStateOfCharge, carSpecs.getBatteryCapacity()));
        // if it is not connected may be running
        // We have a problem in case that the charge exceeds the maximum
        // kWh * km * (kWh/km)
        // Get Consumption is zero if the car was connected
        // only one results is not zero
        double consumption = isConnected* carSpecs.getMaxChargingPerSlot(CommonResources.DEFAULT_INTERVAL) - 
                drivingPattern.getConsumptionPerSlot(index, carSpecs);
        
        // In case we have overload the battery
        consumption -=  isConnected * Math.max(previousStateOfCharge + consumption - carSpecs.getBatteryCapacity(), 0);        
        return consumption;
    }
    
    /**
     * Gets the cost of an slot. Only when is connected (isConnectec= 1) and
     * then there is consumption (consumption > 0).
     * @param consumption
     * @param pricePerkiloWatt
     * @param isConnected
     * @return 
     */
    private double getCost(int isConnected, double consumption, double pricePerkiloWatt){
        logger.info(MessageFormat.format("Cost isConnected {0} consumption {1}  pricePerkiloWatt {2}", 
                isConnected, consumption, pricePerkiloWatt));
        return consumption*pricePerkiloWatt*isConnected;
    }

    /**
     * @return the strategyName
     */
    @Override
    public String getStrategyName() {
        return strategyName+"_"+carName;
    }

    /**
     * @return the slotSize
     */
    @Override
    public int getSlotSize() {
        return slotSize;
    }
}
